package org.xx.armory.commons;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Objects;

import static org.xx.armory.commons.Validators.notBlank;

/**
 * 运算时保留24位精度的十进制类型。
 *
 * @author Haart
 */
public final class Decimal24
        extends Number {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    /**
     * 用于实现舍入控制的上下文，指定了最大24位精度。
     */
    private static final MathContext CONTEXT = new MathContext(24, RoundingMode.HALF_EVEN);

    private BigDecimal value;

    /**
     * 构造Decimal24对象的实例，如果参数{@code value}是{@code null}，那么此实例的值是{@code 0}。
     *
     * @param value
     *         指定Decimal24对象的值的BigDecimal对象。
     */
    private Decimal24(
            BigDecimal value
    ) {
        this.value = value != null ? value : BigDecimal.ZERO;
    }

    /**
     * 将{@code java.math.BigDecimal}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code java.math.BigDecimal}类型的值
     * @return 等价的{@code Decimal24}类型的值，如果{@code value == null}，那么返回值为{@code 0}。
     */
    public static Decimal24 of(
            BigDecimal value
    ) {
        return new Decimal24(value);
    }

    /**
     * 将{@code java.lang.String}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code java.lang.String}类型的值
     * @return 等价的{@code Decimal24}类型的值。
     * @throws IllegalArgumentException
     *         如果{@code value == null}或者只包含空白字符。
     */
    public static Decimal24 of(
            String value
    ) {
        return new Decimal24(new BigDecimal(notBlank(value, "value").trim()));
    }

    /**
     * 将{@code int}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code int}类型的值
     * @return 等价的{@code Decimal24}类型的值。
     */
    public static Decimal24 of(
            int value
    ) {
        return new Decimal24(BigDecimal.valueOf(value));
    }

    /**
     * 将{@code long}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code long}类型的值
     * @return 等价的{@code Decimal24}类型的值。
     */
    public static Decimal24 of(
            long value
    ) {
        return new Decimal24(BigDecimal.valueOf(value));
    }

    /**
     * 将{@code double}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code double}类型的值
     * @return 等价的{@code Decimal24}类型的值。
     */
    public static Decimal24 of(
            double value
    ) {
        return new Decimal24(BigDecimal.valueOf(value));
    }

    /**
     * 将{@code float}类型的值转化为{@code Decimal24}类型的值。
     *
     * @param value
     *         {@code float}类型的值
     * @return 等价的{@code Decimal24}类型的值。
     */
    public static Decimal24 of(
            float value
    ) {
        return new Decimal24(BigDecimal.valueOf(value));
    }

    /**
     * {@inheritDoc}
     *
     * @throws IllegalStateException
     *         如果Decimal24的运算尚未终止。
     */
    @Override
    public int intValue() {
        return value.intValue();
    }

    /**
     * {@inheritDoc}
     *
     * @throws IllegalStateException
     *         如果Decimal24的运算尚未终止。
     */
    @Override
    public long longValue() {
        return value.longValue();
    }

    /**
     * {@inheritDoc}
     *
     * @throws IllegalStateException
     *         如果Decimal24的运算尚未终止。
     */
    @Override
    public float floatValue() {
        return value.floatValue();
    }

    /**
     * {@inheritDoc}
     *
     * @throws IllegalStateException
     *         如果Decimal24的运算尚未终止。
     */
    @Override
    public double doubleValue() {
        return value.doubleValue();
    }

    /**
     * 转化为{@code java.math.BigDecimal}类型。
     *
     * @return 等价的{@code java.math.BigDecimal}类型的值。
     * @throws IllegalStateException
     *         如果Decimal24的运算尚未终止。
     */
    public final BigDecimal decimalValue() {
        return new BigDecimal(this.value.unscaledValue(), this.value.scale());
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}，如果{@code value == null}，那么看作{@code 0}。
     * @throws IllegalStateException
     *         如果Decimal24的运算已经终止。
     */
    public final Decimal24 add(
            BigDecimal value
    ) {
        if (value != null) {
            this.value = this.value.add(value);
        }
        return this;
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}，如果{@code value == null}，那么看作{@code 0}。
     */
    public final Decimal24 add(
            Decimal24 value
    ) {
        if (value != null) {
            this.value = this.value.add(value.value);
        }
        return this;
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}。
     */
    public final Decimal24 add(
            int value
    ) {
        this.value = this.value.add(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}。
     */
    public final Decimal24 add(
            long value
    ) {
        this.value = this.value.add(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}。
     */
    public final Decimal24 add(
            float value
    ) {
        this.value = this.value.add(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相加。
     *
     * @param value
     *         加数。
     * @return {@code this + value}。
     */
    public final Decimal24 add(
            double value
    ) {
        this.value = this.value.add(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}，如果{@code value == null}，那么看作{@code 0}。
     */
    public final Decimal24 subtract(
            BigDecimal value
    ) {
        if (value != null) {
            this.value = this.value.subtract(value);
        }
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}，如果{@code value == null}，那么看作{@code 0}。
     */
    public final Decimal24 subtract(
            Decimal24 value
    ) {
        if (value != null) {
            this.value = this.value.subtract(value.value);
        }
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}。
     */
    public final Decimal24 subtract(
            int value
    ) {
        this.value = this.value.subtract(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}。
     */
    public final Decimal24 subtract(
            long value
    ) {
        this.value = this.value.subtract(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}。
     */
    public final Decimal24 subtract(
            float value
    ) {
        this.value = this.value.subtract(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相减。
     *
     * @param value
     *         减数。
     * @return {@code this - value}。
     */
    public final Decimal24 subtract(
            double value
    ) {
        this.value = this.value.subtract(BigDecimal.valueOf(value));
        return this;
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}，如果{@code value == null}，那么看作{@code 1}。
     */
    public final Decimal24 multiply(
            BigDecimal value
    ) {
        if (value != null) {
            this.value = this.value.multiply(value);
        }
        return this;
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}，如果{@code value == null}，那么看作{@code 1}。
     */
    public final Decimal24 multiply(
            Decimal24 value
    ) {
        return multiply(value.value);
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}。
     */
    public final Decimal24 multiply(
            int value
    ) {
        switch (value) {
            case 1:
                // 乘以1等于自身。
                break;
            case 10:
                this.value = this.value.movePointRight(1);
                break;
            case 100:
                this.value = this.value.movePointRight(2);
                break;
            case 1000:
                this.value = this.value.movePointRight(3);
                break;
            case 10000:
                this.value = this.value.movePointRight(4);
                break;
            case 100000:
                this.value = this.value.movePointRight(5);
                break;
            case 1000000:
                this.value = this.value.movePointRight(6);
                break;
            case 10000000:
                this.value = this.value.movePointRight(7);
                break;
            case 100000000:
                this.value = this.value.movePointRight(8);
                break;
            default:
                return multiply(BigDecimal.valueOf(value));
        }
        return this;
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}。
     */
    public final Decimal24 multiply(
            long value
    ) {
        return (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) ? multiply((int) value) : multiply(BigDecimal.valueOf(value));
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}。
     */
    public final Decimal24 multiply(
            float value
    ) {
        return multiply(BigDecimal.valueOf(value));
    }

    /**
     * 执行两个数相乘。
     *
     * @param value
     *         乘数。
     * @return {@code this * value}。
     */
    public final Decimal24 multiply(
            double value
    ) {
        return multiply(BigDecimal.valueOf(value));
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}，如果{@code value == null}，那么看作{@code 1}。
     */
    public final Decimal24 divide(
            BigDecimal value
    ) {
        if (value != null) {
            this.value = this.value.divide(value, CONTEXT);
        }
        return this;
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}，如果{@code value == null}，那么看作{@code 1}。
     */
    public final Decimal24 divide(
            Decimal24 value
    ) {
        return divide(value.value);
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}。
     */
    public final Decimal24 divide(
            int value
    ) {
        switch (value) {
            case 1:
                // 除以1等于自身。
                break;
            case 10:
                this.value = this.value.movePointLeft(1);
                break;
            case 100:
                this.value = this.value.movePointLeft(2);
                break;
            case 1000:
                this.value = this.value.movePointLeft(3);
                break;
            case 10000:
                this.value = this.value.movePointLeft(4);
                break;
            case 100000:
                this.value = this.value.movePointLeft(5);
                break;
            case 1000000:
                this.value = this.value.movePointLeft(6);
                break;
            case 10000000:
                this.value = this.value.movePointLeft(7);
                break;
            case 100000000:
                this.value = this.value.movePointLeft(8);
                break;
            default:
                return divide(BigDecimal.valueOf(value));
        }
        return this;
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}。
     */
    public final Decimal24 divide(
            long value
    ) {
        return (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) ? divide((int) value) : divide(BigDecimal.valueOf(value));
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}。
     */
    public final Decimal24 divide(
            float value
    ) {
        return divide(BigDecimal.valueOf(value));
    }

    /**
     * 执行两个数相除。
     *
     * @param value
     *         除数。
     * @return {@code this / value}。
     */
    public final Decimal24 divide(
            double value
    ) {
        return divide(BigDecimal.valueOf(value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return this.value == null ? 0 : this.value.hashCode();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(
            Object obj
    ) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Decimal24))
            return false;

        final Decimal24 other = (Decimal24) obj;
        return Objects.equals(this.value, other.value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return this.value != null ? this.value.toString() : "";
    }
}
